/*
	Modification Infomation:
		1. change to Unicode in 20/08/2007 as adviced by TD
		2. submitted a working without menu edition in 20/08/2007
		3. change to a wchar_t to TCHAR
	23/08/2007
		1. C++ stl is introduced into this project
	24/08/2007
		1. Got a problem when default maximum the child window 
		// fixed because of some message should return to default callback function
		2. In the section of finding a index of a certain tab and 
			remove corresponding handler from the vector, is there a easier way ?			
		3. Detect a huge mistake which is : I set a ID to be 70000 which is more than 65536, 
			and make a default behavior fail to work!

*/

#include	<windows.h>
#include	<tchar.h>
#include	<commctrl.h>			// Import library:	comctl32.lib
#include	<stdio.h>				
#include	<vector>
#include	"resource.h"
#include	"utility.h"

#define		IDC_MAIN_TOOLBAR				1000
#define		IDC_MAIN_EDIT					1001		// resource.h is not a good place to place these!!!
#define		IDC_MAIN_MDI					1002
#define		IDC_MAIN_STATUS					1003
#define		ID_MDI_FIRSTCHILD				60000		// a big enough id for the children windows
#define		WRATIO							2/3			// to avoid the float to int conversion warning, use 2/3 instead of 0.7 
#define		TABCTRLHEIGHT					30			// the height of the common tab control

using namespace std;

const TCHAR g_szClassName[] = _T("myWindowClass");
const TCHAR g_szChildClassName[] = _T("myMDIChildWindowClass");

HINSTANCE hInst = NULL;					// handle to application instance 
HWND g_hMainWindow = NULL;				// main application window 
HWND g_hMDIClient = NULL;				// MDI client of main window
HWND g_hwndTab = NULL;					// tab control of main window
HWND g_hStatus = NULL;					// status bar of the window
HWND g_hDialog = NULL;					// Modeless Dialog for friend list
vector<HWND> hcwndv;					// store client windows in vector


// Forward Declarations of some functions
BOOL SetUpMainWindowClass(HINSTANCE hInstance);
BOOL SetUpMDIChildWindowClass(HINSTANCE hInstance);
HWND CreateMDIChildWindow(HWND hMDIClient);
HWND WINAPI DoCreateTabControl(HWND hwndParent);
BOOL DoAddTab(size_t i, const TCHAR * text);

// Message handler for tab dialog.
INT_PTR CALLBACK TabDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

BOOL CALLBACK DialogProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	switch(Message)
	{
		case WM_COMMAND:
			switch(LOWORD(wParam))
			{
				/*
				case IDC_PRESS:
					MessageBox(hwnd, "Hi!", "This is a message", 
						MB_OK | MB_ICONEXCLAMATION);
				break;
				case IDC_OTHER:
					MessageBox(hwnd, "Bye!", "This is also a message", 
						MB_OK | MB_ICONEXCLAMATION);
				break;
				*/
			}
		break;
		default:
			return FALSE;
	}
	return TRUE;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
		case WM_CREATE:
		{
			// Create MDI client for the main window
			CLIENTCREATESTRUCT ccs;
			ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), 3);	// where the opened file will appear
			ccs.idFirstChild = ID_MDI_FIRSTCHILD;			// first id set to be big enough for identify children window

			//RECT rcRect;
			//GetClientRect(hwnd, &rcRect);

			g_hMDIClient = CreateWindowEx(WS_EX_CLIENTEDGE, _T("mdiclient"), NULL, 
				WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, // | WS_VSCROLL | WS_HSCROLL 
				//0, TABCTRLHEIGHT, rcRect.right, rcRect.bottom,		// leave a certain space for tab control
				CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
				hwnd, NULL, hInst, (LPVOID)&ccs);
			if(g_hMDIClient == NULL)
			{
				MessageBox(hwnd, _T("Could not create MDI Client."), _T("Error"), MB_OK | MB_ICONERROR);
			}

			// Create Tab control for the main window
			g_hwndTab = DoCreateTabControl(hwnd);
			if(g_hwndTab)
			{
				DoAddTab( 0, _T("Tab 1") );
			}
			
			//Create the first client window
			HWND hChild = CreateMDIChildWindow(g_hMDIClient);
			if(hChild)
			{
				hcwndv.push_back(hChild);
			}

			// Create a status bar
			g_hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
				0, 0, 0, 0,	hwnd, (HMENU)IDC_MAIN_STATUS, hInst, NULL);
			int statusbar[] = {128, -1};
			SendMessage(g_hStatus, SB_SETPARTS, sizeof(statusbar)/sizeof(int), (LPARAM)statusbar);
			SendMessage(g_hStatus, SB_SETTEXT, 0, (LPARAM)_T("Status Bar :)"));
			SendMessage(g_hStatus, SB_SETTEXT, 1, (LPARAM)_T("Message Might appear here!"));

			// Create a Modeless Dialog to display friend list
			g_hDialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_DIALOG_FL), hwnd, DialogProc);
			if(g_hDialog)
			{
				ShowWindow(g_hDialog, SW_SHOW);
			}
			else
			{ 
				MessageBox(hwnd, _T("Error when creating Modeless Dialog!"), NULL, MB_OK | MB_ICONINFORMATION);
			}
			break;
		}

		case WM_COMMAND:
			switch(LOWORD(wParam))
			{
				case ID_FILE_NEWF:
				{
					HWND hChild = CreateMDIChildWindow(g_hMDIClient);
					if(hChild)
					{
						hcwndv.push_back(hChild);			
						DoAddTab( hcwndv.size(), _T("New Tab") );
					}
					break;
				}
				case ID_FILE_OPENF:	// will make it more intelligent to adjust whether a unused page exist
				{
					HWND hChild = CreateMDIChildWindow(g_hMDIClient);
					if(hChild)
					{
						hcwndv.push_back(hChild);
						Utility utility(hChild);
						utility.DoFileOpen();	
						DoAddTab(hcwndv.size(), utility.GetFileName());
						SendDlgItemMessage(g_hMainWindow, IDC_MAIN_STATUS, SB_SETTEXT, 0, (LPARAM)_T("Opened...") );
						SendDlgItemMessage(g_hMainWindow, IDC_MAIN_STATUS, SB_SETTEXT, 1, (LPARAM)utility.GetFileName());
					}
					break;
				}
				case ID_FILE_SAVEAS:
				{
					break;
				}
				case ID_VIEW_TILE:
					SendMessage(g_hMDIClient, WM_MDITILE, MDITILE_HORIZONTAL, NULL);
					break;
				case ID_VIEW_CASCADE:
					SendMessage(g_hMDIClient, WM_MDICASCADE, MDITILE_SKIPDISABLED, NULL);					
					break;
				case ID_VIEW_TILEVERTICAL:
					SendMessage(g_hMDIClient, WM_MDITILE, MDITILE_VERTICAL, NULL);
					break;
				case ID_FILE_EXIT:
					PostMessage(hwnd, WM_CLOSE, 0, 0);
					break;
				default:	// deal with other message, to be done in the future
				if(LOWORD(wParam) >= ID_MDI_FIRSTCHILD)
				{
					// to set the corresponding tab selected when change the active window by menu
					// and the key of CTRL + F5/6 to be handled in the future edition
					SendMessage(g_hwndTab, TCM_SETCURSEL, LOWORD(wParam) - ID_MDI_FIRSTCHILD, NULL);
					DefFrameProc(hwnd, g_hMDIClient, msg, wParam, lParam);
				}
				else
				{
					HWND hChild = (HWND)SendMessage(g_hMDIClient, WM_MDIGETACTIVE, 0, 0);
					if(hChild)
					{
						SendMessage(hChild, WM_COMMAND, wParam, lParam);
					}
				}
			}
			return DefFrameProc(hwnd, g_hMDIClient, msg, wParam, lParam);
		  
		case WM_NOTIFY:
            switch (((LPNMHDR)lParam)->code)
            {
				case TCN_SELCHANGE:
                { 
                    size_t iPage = TabCtrl_GetCurSel(g_hwndTab); 
					// activate the corresponding window
					SendMessage(g_hMDIClient, WM_MDIACTIVATE, (WPARAM)hcwndv[iPage], NULL);
                    break;
                } 
            }

		case WM_LBUTTONDOWN:
		{
			/*
			TCHAR szFileName[MAX_PATH];
			HINSTANCE hInstance = GetModuleHandle(NULL);
			GetModuleFileName(hInstance, szFileName, MAX_PATH);
			MessageBox(hwnd, szFileName, _T("This program is:"), MB_OK | MB_ICONINFORMATION);
			*/
			break;
		}

		case WM_SIZE:
		{
			RECT rcClient, statusRect;
			// get the client area of the main window
			GetClientRect(hwnd, &rcClient);
			// size the status bar and get the client rect
			SendMessage(g_hStatus, WM_SIZE, NULL, NULL);	
			GetWindowRect(g_hStatus, &statusRect);
			// size the tab area and the MDI client area
			// several magic number used here to make the window looks more pretty
			SetWindowPos(g_hwndTab, NULL, 0, 0, rcClient.right + 2, TABCTRLHEIGHT, SWP_NOMOVE | SWP_NOZORDER);
			SetWindowPos(g_hMDIClient, NULL, -2, TABCTRLHEIGHT - 2, rcClient.right + 4, 
				rcClient.bottom + statusRect.top - statusRect.bottom - TABCTRLHEIGHT + 4, SWP_NOZORDER);
			
			// size the modeless dialog
			// SetWindowPos(g_hDialog, NULL, rcClient.left + 270, rcClient.top + 30, rcClient.left + 470, rcClient.top + 700, SWP_NOZORDER);
			break;
		}
		case WM_CLOSE:
			DestroyWindow(hwnd);
			break;
		case WM_DESTROY:
			DestroyWindow(g_hDialog);
			PostQuitMessage(0);
			break;
		default:
			// return DefWindowProc(hwnd, msg, wParam, lParam);
			return DefFrameProc(hwnd, g_hMDIClient, msg, wParam, lParam);
	}
	return 0;
}

LRESULT CALLBACK MDIChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
		case WM_CREATE:
		{
			HFONT hfDefault;
			HWND hEdit;

			// use the style of maximize size instead of caculate the client rectangle
			hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, _T("EDIT"), _T(""), 
				WS_CHILD |  WS_MAXIMIZE | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
				0, 0, 0, 0, hwnd, (HMENU)IDC_MAIN_EDIT, hInst, NULL);
			if(hEdit == NULL)
			{
				MessageBox(hwnd, _T("Could not create edit box."), _T("Error"), MB_OK | MB_ICONERROR);
			}

			hfDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
			SendMessage(hEdit, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
			break;
		}
		case WM_CLOSE:
		{
			// Find the index of the tab
			size_t i;
			for ( i = 0; i < hcwndv.size(); i++ )
			{
				if(hcwndv[i] == hwnd) {
					SendMessage(g_hwndTab, TCM_DELETEITEM, (WPARAM)i, NULL);
					break;
				}
			}
			// remove the corresponding handler in the vector
			vector<HWND>::iterator Iter;
			for ( Iter = hcwndv.begin( ) ; Iter != hcwndv.end( ) ; Iter++ )
			{
				if(*Iter == hwnd)
				{
					hcwndv.erase(Iter);
					break;
				}
			}
			return DefMDIChildProc(hwnd, msg, wParam, lParam);
		}
		case WM_SIZE:
		{
			RECT rcRect;
			HWND hEdit;
			GetClientRect(hwnd, &rcRect);
			hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
			SetWindowPos(hEdit, NULL, -1, -1, rcRect.right + 1, rcRect.bottom + 1, SWP_NOZORDER);
			// MSDN: Failing to pass this message to the DefMDIChildProc function produces highly undesirable results.
			return DefMDIChildProc(hwnd, msg, wParam, lParam);	
		}
		default:
			return DefMDIChildProc(hwnd, msg, wParam, lParam);
	}

	return 0;
}

HWND CreateMDIChildWindow(HWND hMDIClient)
{
	MDICREATESTRUCT mcs;
	HWND hChild = NULL;

    mcs.x = mcs.cx = CW_USEDEFAULT; 
    mcs.y = mcs.cy = CW_USEDEFAULT; 
	mcs.szTitle = _T("Untitled");
	mcs.szClass = g_szChildClassName;
	mcs.hOwner = GetModuleHandle(NULL);
	mcs.style = MDIS_ALLCHILDSTYLES | WS_MAXIMIZE;

	hChild = (HWND)SendMessage(hMDIClient, WM_MDICREATE, 0, (LPARAM)&mcs);
	if(hChild == NULL)
	{
		MessageBox(hMDIClient, _T("MDI Child Window Fail to Create."), _T("Error"), MB_ICONEXCLAMATION | MB_OK);
	}
	return hChild;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	HWND hwnd;
	MSG msg;

	hInst = hInstance;
	// Step 1: Registering the Window CLass
	if(!SetUpMainWindowClass(hInstance) || !SetUpMDIChildWindowClass(hInstance))
		return 0;

	// Step 2: Creating the Window
	hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, g_szClassName, _T("The title of my window"), 
		WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS,
		CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInst, NULL);

	if(hwnd == NULL)
	{
		MessageBox(NULL, _T("Window Creation Failed!"), _T("Error!"), MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}
	g_hMainWindow = hwnd;	// save the main window handler to a global variable

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	// Step 3: The Message Loop
	while(GetMessage(&msg, NULL, 0, 0) > 0)
	{
		if(!IsDialogMessage(g_hDialog, &msg))	// Modeless Dialog
		{
			if(!TranslateMDISysAccel(g_hMDIClient, &msg))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
	}
	return (int)msg.wParam;
}

BOOL SetUpMainWindowClass(HINSTANCE hInstance)
{
	WNDCLASSEX wc;

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = 0;
	wc.lpfnWndProc = WndProc;	// the callback function in the very beginning...
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
	wc.lpszClassName = g_szClassName;	// the global variable defined in the beginning...
	wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

	if(!RegisterClassEx(&wc))
	{
		MessageBox(NULL, _T("Main Window CLass Registration Failed!"), _T("Error!"), MB_ICONEXCLAMATION | MB_OK);
		return FALSE;
	}
	return TRUE;
}

BOOL SetUpMDIChildWindowClass(HINSTANCE hInstance)
{
	WNDCLASSEX wc;

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = MDIChildWndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = g_szChildClassName;
	wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

	if(!RegisterClassEx(&wc))
	{
		MessageBox(0, _T("Could not register Child Window Class!"), _T("Error"),	MB_ICONEXCLAMATION | MB_OK);
		return FALSE;
	}
	return TRUE;	
}

// Copied from the example of MSDN, and modified for this program, may change in the future
// DoCreateTabControl - creates a tab control, sized to fit the 
// specified parent window's client area, and adds some tabs. 
// Returns the handle to the tab control. 
// hwndParent - parent window (the application's main window). 
HWND WINAPI DoCreateTabControl(HWND hwndParent) 
{ 
    //RECT rcClient; 
    HWND hwndTab;		// handler of the tab control

    // Get the dimensions of the parent window's client area
    // GetClientRect(hwndParent, &rcClient); 
	// To ensure that the DLL is loaded, use the InitCommonControls function.
	INITCOMMONCONTROLSEX ctrl;		
	ctrl.dwSize = sizeof(ctrl);
	ctrl.dwICC = ICC_TAB_CLASSES | ICC_BAR_CLASSES;
	InitCommonControlsEx(&ctrl);
	// Create a tab control child window.
	
    hwndTab = CreateWindowEx(NULL, WC_TABCONTROL, _T(""), 
        WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 
        //0, 0, rcClient.right, rcClient.bottom, 
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        hwndParent, NULL, hInst, NULL); 

    return hwndTab; 
} 

// for dynamic add tab to the tab control, seperate this piece of code
BOOL DoAddTab(size_t i, const TCHAR * text)
{
	// Add a tab
	TCHAR pText[MAX_PATH];
	_tcsncpy_s(pText, text, MAX_PATH);

    TCITEM tie; 
    tie.mask = TCIF_TEXT | TCIF_IMAGE; 
    tie.iImage = -1; 
    tie.pszText = pText;
	tie.cchTextMax = MAX_PATH;

	if (TabCtrl_InsertItem(g_hwndTab, i, &tie) == -1) 
    { 
        DestroyWindow(g_hwndTab); 
		return FALSE; 
    }
	return TRUE;
}